#include <stdio.h>      
#include <stdlib.h>     // exit
#include <string.h>    
#include <unistd.h>     // close
#include <pthread.h>    
#include <netinet/in.h> // sockaddr_in INADDR_ANY htons

#define PORT 8889              // 服务器监听端口
#define MAX_CLIENTS 5          // 最大客户端连接数
#define BUF_SIZE 1024          // 缓冲区大小

// step 1# 定义客户端结构体，包含套接字与用户名
typedef struct {
    int socket;                 // 客户端套接字描述符
    char username[50];          // 客户端用户名
} Client;

Client clients[MAX_CLIENTS];   // 保存所有连接的客户端信息
int client_count = 0;          // 当前连接的客户端数量
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; // 初始化互斥锁，保护共享资源

// step 2# 向所有客户端发送消息
void send_to_all(const char *msg) {
    pthread_mutex_lock(&lock);  // 加锁，防止并发写冲突
    for (int i = 0; i < client_count; i++) {
        send(clients[i].socket, msg, strlen(msg), 0); // 逐个发送消息
    }
    pthread_mutex_unlock(&lock); // 解锁
}

// step 3# 发送当前在线用户列表给指定客户端
void send_online_users(int client_sock) {
    char list[BUF_SIZE] = "在线用户：\n";
    pthread_mutex_lock(&lock);  // 加锁，遍历客户端列表
    for (int i = 0; i < client_count; i++) {
        strcat(list, clients[i].username);  // 拼接用户名
        strcat(list, "\n");
    }
    pthread_mutex_unlock(&lock); // 解锁
    send(client_sock, list, strlen(list), 0); // 发送用户列表
}

// step 4# 处理每个客户端连接的线程函数
void *handle_client(void *arg) {
    int sock = *(int *)arg;                        // 获取客户端套接字
    char buffer[BUF_SIZE], username[50], target[50];

    // step 4.1# 接收客户端用户名
    recv(sock, username, sizeof(username), 0);

    // step 4.2# 添加客户端信息到全局数组
    pthread_mutex_lock(&lock);
    strcpy(clients[client_count].username, username); // 保存用户名
    clients[client_count].socket = sock;              // 保存套接字
    client_count++;                                   // 客户端计数加一
    pthread_mutex_unlock(&lock);

    // step 4.3# 通知所有人该用户上线
    sprintf(buffer, "[系统] %s 上线啦！\n", username);
    send_to_all(buffer);

    // step 4.4# 循环接收客户端消息
    while (1) {
        memset(buffer, 0, BUF_SIZE);                 // 清空缓冲区
        int len = recv(sock, buffer, BUF_SIZE, 0);   // 接收数据
        if (len <= 0) break;                         // 客户端断开连接

        // step 4.5# 处理特殊命令
        if (strncmp(buffer, "/list", 5) == 0) {
            send_online_users(sock); // 客户端请求在线用户列表
        } else if (strncmp(buffer, "/to ", 4) == 0) {
            // step 4.6# 处理私聊命令 /to 用户名 消息
            sscanf(buffer, "/to %s %[^\n]", target, buffer); // 解析目标用户名与消息
            pthread_mutex_lock(&lock);
            for (int i = 0; i < client_count; i++) {
                if (strcmp(clients[i].username, target) == 0) {
                    char msg[BUF_SIZE];
                    sprintf(msg, "[私信来自 %s] %s\n", username, buffer);
                    send(clients[i].socket, msg, strlen(msg), 0); // 发送私聊消息
                    break;
                }
            }
            pthread_mutex_unlock(&lock);
        } else if (strncmp(buffer, "/notice ", 8) == 0) {
            // step 4.7# 处理公告命令
            char notice[BUF_SIZE];
            snprintf(notice, BUF_SIZE, "[公告] %s\n", buffer + 8); // 构造公告内容
            send_to_all(notice); // 广播公告
        }
    }

    // step 4.8# 客户端退出，清理资源
    close(sock); // 关闭套接字
    pthread_mutex_lock(&lock);
    for (int i = 0; i < client_count; i++) {
        if (clients[i].socket == sock) {
            clients[i] = clients[--client_count]; // 用最后一个客户端替换并减少数量
            break;
        }
    }
    pthread_mutex_unlock(&lock);
    return NULL;
}

// step 5# 服务器主函数，监听客户端连接
int main() {
    int server_sock = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字
    struct sockaddr_in server = {
        .sin_family = AF_INET,                         // IPv4
        .sin_port = htons(PORT),                       // 端口号（网络字节序）
        .sin_addr.s_addr = INADDR_ANY                  // 接收任意地址连接
    };

    bind(server_sock, (struct sockaddr *)&server, sizeof(server)); // 绑定地址和端口
    listen(server_sock, MAX_CLIENTS);                              // 开始监听
    printf("服务器已启动，端口 %d\n", PORT);

    // step 5.1# 循环接受客户端连接
    while (1) {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        int client_sock = accept(server_sock, (struct sockaddr *)&client, &len); // 接受连接
        pthread_t tid;
        pthread_create(&tid, NULL, handle_client, &client_sock); // 创建线程处理客户端
        pthread_detach(tid);                                     // 分离线程，自动回收
    }
    return 0;
}
